const common = require('./common')
const {getResult, getError} = require('./handle')

// 随机生成n位字符串。可能包含数字和大小写字母
const generateRandomString = (length = 32) => {
    let result = '';
    
    for (let i = 0; i < length; i++) {
        // 随机决定字符类型：0-数字，1-大写字母，2-小写字母
        const charType = Math.floor(Math.random() * 3);
        let asciiCode;
        
        switch (charType) {
            case 0: // 数字 (48-57: '0'-'9')
                asciiCode = Math.floor(Math.random() * 10) + 48;
                break;
            case 1: // 大写字母 (65-90: 'A'-'Z')
                asciiCode = Math.floor(Math.random() * 26) + 65;
                break;
            case 2: // 小写字母 (97-122: 'a'-'z')
                asciiCode = Math.floor(Math.random() * 26) + 97;
                break;
        }
        
        result += String.fromCharCode(asciiCode);
    }
    
    return result;
}
/**
 * 获取当前用户ID的随机四位值
 */
const getUserId = (uid = common.necessary.User) => {
	if (!uid || uid.length < 4) {
		return generateRandomString(4)
	}
	var res = ''
	for (let i = 0; i < 4; i++) {
		res += uid[Math.floor(Math.random() * uid.length)]
	}
	return res
}

/**
 * 获取当前的年月日时分秒和毫秒
 * sortYear：是否只要后两位的年份
 */
const getTimeObj = (sortYear) => {
	const date = new Date()
	var year = date.getFullYear(),
		month = date.getMonth() + 1,
		day = date.getDate(),
		hour = date.getHours(),
		minute = date.getMinutes(),
		second = date.getSeconds(),
		msec = date.getMilliseconds(),
		week = date.getDay()
	year = year.toString()
	if (sortYear) {
		year = year.substr(-2)
	}
	if (month < 10) {
		month = '0' + month
	} else {
		month = month.toString()
	}
	if (day < 10) {
		day = '0' + day
	} else {
		day = day.toString()
	}
	if (hour < 10) {
		hour = '0' + hour
	} else {
		hour = hour.toString()
	}
	if (minute < 10) {
		minute = '0' + minute
	} else {
		minute = minute.toString()
	}
	if (second < 10) {
		second = '0' + second
	} else {
		second = second.toString()
	}
	if (msec < 10) {
		msec = '00' + msec
	} else if (msec < 100) {
		msec = '0' + msec
	} else {
		msec = msec.toString()
	}
	return {
		year, month, day, hour, minute, second, msec
	}
}

/**
 * 生成一个随机的数字或者字母
 * 48-57是0-9
 * 65-90是A-Z
 * 97-122是a-z
 * 
 * type：1-数字；2-小写；3-大写
 * 4-数字+小写，5-数字+大写，6-小写+大写，7-数字+小写+大写
 */
const getRandom = (type) => {
	var length = 10, start = 48
	switch (type){
		case 1: // 数字
			length = 10
			start = 48
			break;
		case 2: // 小写
			length = 26
			start = 97
			break;
		case 3: // 大写
			length = 26
			start = 65
			break;
		case 4: // 数字+小写
		{
			const random = Math.floor(Math.random() * 36)
			if (random < 10) {
				return getRandom(1)
			}
			return getRandom(2)
		}
		case 5: // 数字+大写
		{
			const random = Math.floor(Math.random() * 36)
			if (random < 10) {
				return getRandom(1)
			}
			return getRandom(3)
		}
		case 6: // 小写+大写
		{
			const random = Math.floor(Math.random() * 2)
			if (random < 1) {
				return getRandom(2)
			}
			return getRandom(3)
		}
		case 7: // 数字+小写+大写
		{
			const random = Math.floor(Math.random() * 62)
			if (random < 10) {
				return getRandom(1)
			} else if (random < 36) {
				return getRandom(2)
			}
			return getRandom(3)
		}
		default:
			break;
	}
	var res = Math.floor(Math.random() * length) + start
	res = String.fromCharCode(res)
	return res
}

/**
 * 获取编码。当验证属于重复时需要重新生成，只要唯一
 * mode: 编码模式。1-纯数字，2-纯小写，3-纯大写，4-数字+小写，5-数字+大写，6-小写+大写，7-数字+小写+大写
 * length: 编码长度。默认32位
 */
const getCode = (data = {}) => {
	var {
		type = 1, // 要生成的编码类型。0-短编码，1-默认编码，2-简洁编码，3-层级编码，4-其它未定义编码
		uid, // 用户id或者原始值
		mode, // 该编码模式。1-纯数字，2-纯小写，3-纯大写，4-数字+小写，5-数字+大写，6-小写+大写，7-数字+小写+大写
		length, // 额外长度
		
		BM = '', // 编码前缀
		XM = common.necessary.Prefix || '', // 项目前缀
		
		space, // 云服务名称
		name, // 数据库名称
		remark, // 编码的备注信息
		
		register, // 是否注册到数据库以备案。1或者true返回值是Promise，默认是验证并且备案；0或者false返回值是字符串。2-仅验证不备案
		code = '', // 如果该code已存在，说明是用来注册的。不过注册前还是需要验证一遍
	} = data
	if (code) { // code已存在，说明是用来注册备案的。不过注册前还是需要验证一遍
		register = true
	} else {
		switch (type){
			case 0: // 短编码。优先使用原始值，只有原始值不满足唯一时再添加随机值。而且该编码值的备案需手动确认
			{
				if (!mode) { // 默认是小写字母
					mode = 2
				}
				if (!uid) { // 不存在原始值时，先随机生成一个五位的编码
					uid = ''
					for (let i = 0; i < 5; i++) {
						uid += getRandom(mode) // 每一位都在对应模式内随机
					}
				}
				code = uid
				if (!length) { // 默认两位
					length = 2
				}
				if (length > 0 && data.error > 0) {
					for (let i = 0; i < length; i++) {
						code += getRandom(1) // 后面的随机值默认是数字
					}
				}
			}
				break;
			case 1: // 默认编码
			case 2: // 简洁编码
			{
				if (!mode) { // 默认是小写字母
					mode = 2
				}
				const userId = getUserId(uid)
				const {year, month, day, hour, minute, second, msec} = getTimeObj(true)
				const letter = getRandom(mode)
				const number = getRandom(1)
				if (type === 1) {
					/**
					 * 第一部分是年月日时（根据时区的特点，精确到时可以修正日期）
					 * 第二部分是分秒毫秒+随机字符
					 * 24110112${编码前缀}-${项目前缀}3922742${用户ID}${随机字母}${随机数字}${校验码}
					 */
					code = `${year}${month}${day}${hour}${BM}-${XM}${minute}${second}${msec}${userId}${letter}${number}`
				} else {
					/**
					 * 获取当前的三位毫秒值
					 * ${编码前缀}742${用户ID}${随机字母}${随机数字}${项目前缀}${校验码}
					 */
					code = `${BM}${msec}${userId}${letter}${number}${XM}`
				}
				/**
				 * 得到校验码
				 * 每一位的编码值加起来再同36（10个数字+26个大写字母）取余
				 */
				var res = 0
				for (let i = 0; i < code.length; i++) {
					res += code.charCodeAt(i)
				}
				res = res % 36
				if (res > 10) { // 大于10的，转化为小写字母或大写字母（确定没有小写时才使用大写）
					res -= 10
					res = String.fromCharCode(res + (mode === 3 || mode === 5 ? 65 : 97))
				}
				code += res
			}
				break;
			case 3: // 层级编码。需根据父级编码生成下级编码
			{
				if (!mode) { // 默认是大写字母
					mode = 5
				}
				if (!length) { // 默认3位
					length = 3
				}
				code = uid || ''
				for (let i = 0; i < length; i++) {
					code += getRandom(mode)
				}
			}
				break;
			case 4: // 自定义编码。比如项目前缀那种，约定长度为2，数字+大写
			{
				if (!mode) { // 默认数字+大写
					mode = 5
				}
				if (!length) { // 默认2位
					length = 2
				}
				for (let i = 0; i < length; i++) {
					code += getRandom(mode)
				}
			}
				break;
			default:
				break;
		}
	}
	if (register === undefined) { // 1和2，默认备案；其它默认不备案
		data.register = register = (type === 1 || type === 2) ? 1 : 2
	}
	if (register) {
		return new Promise((resolve, reject) => {
			if (code) {
				const db = uniCloud.database().collection('a-a-code')
				db.where({code}).count().then(res => {
					if (res.total > 0) { // 该编码已存在，要重新生成，最多执行五次
						if (data.error) {
							data.error += 1
						} else {
							data.error = 1
						}
						if (data.error >= 5) {
							reject(getError('该类型编码已超限'))
						} else {
							getCode(data).then(resolve).catch(reject)
						}
					} else if (register === 2) { // 先不备案，需手动确认再备案。比如短编码
						resolve(getResult({data: code}))
					} else { // 对该编码备案
						db.add({code, type, space, name, remark, target: common.necessary.Target}).then(res => {
							resolve(getResult({data: code}))
						}).catch(reject)
					}
				}).catch(reject)
			} else {
				reject(getError('编码生成失败'))
			}
		})
	}
	return code
}

/**
 * 获取唯一的编码
 * 返回值是Promise
 */
module.exports = getCode